/*
 * ========================================
 * RISC-V 最小操作系统串口驱动
 * ========================================
 * 
 * 这个文件实现了UART (Universal Asynchronous Receiver/Transmitter) 串口驱动。
 * UART是计算机与外部设备通信的重要接口，在我们的最小操作系统中，
 * 它用于向QEMU的虚拟终端输出文本信息。
 * 
 * UART 16550 是经典的串口控制器，具有以下特点：
 * - 支持异步串行通信
 * - 内置FIFO缓冲区
 * - 可编程的波特率
 * - 中断支持
 * 
 * 在我们的最小实现中，我们只使用最基本的输出功能。
 */

// 包含必要的头文件
#include "../include/types.h"      // 基本数据类型定义
#include "../include/memlayout.h"  // 内存布局定义，包含UART0地址

// ========================================
// UART 16550 寄存器定义
// ========================================
//
// UART控制器通过内存映射的方式访问，每个寄存器占用1字节
// 基地址是UART0 (0x10000000)，各个寄存器通过偏移量访问

// THR (Transmit Holding Register) - 发送保持寄存器
// 偏移量：0
// 功能：写入要发送的数据字节
#define UART_THR 0

// LSR (Line Status Register) - 线路状态寄存器  
// 偏移量：5
// 功能：显示UART的状态信息，如发送是否完成
#define UART_LSR 5

// LSR寄存器的第5位：THRE (Transmit Holding Register Empty)
// 当这个位为1时，表示发送保持寄存器为空，可以发送下一个字节
#define LSR_TX_IDLE (1<<5)

// ========================================
// UART寄存器访问宏定义
// ========================================
//
// 这些宏简化了UART寄存器的读写操作
// volatile关键字告诉编译器不要优化这些内存访问

// 获取UART寄存器的地址
// reg：寄存器偏移量
// 返回：指向该寄存器的指针
#define UART_REG(reg) ((volatile uint8*)(UART0 + (reg)))

// 读取UART寄存器
// reg：寄存器偏移量
// 返回：寄存器的值
#define ReadReg(reg) (*(UART_REG(reg)))

// 写入UART寄存器
// reg：寄存器偏移量
// v：要写入的值
#define WriteReg(reg, v) (*(UART_REG(reg)) = (v))

// ========================================
// UART初始化函数
// ========================================
//
// 初始化UART控制器，设置基本的工作参数
// 在我们的最小实现中，使用UART的默认设置即可
void uart_init(void)
{
    // 对于我们的最小操作系统实现，我们不需要复杂的UART初始化
    // QEMU的UART默认配置已经足够进行基本的字符输出
    
    // 在更完整的实现中，这里会设置：
    // - 波特率（数据传输速度）
    // - 数据位、停止位、校验位
    // - FIFO缓冲区
    // - 中断使能
    //
    // 但我们的目标是理解操作系统启动机制，所以保持简单
}

// ========================================
// 单字符输出函数
// ========================================
//
// 向UART发送一个字符
// c：要发送的字符
void uart_putc(char c)
{
    // ========================================
    // 等待发送寄存器空闲
    // ========================================
    //
    // 为什么需要等待？
    // - UART发送数据需要时间
    // - 如果前一个字符还没发送完就发送下一个，会导致数据丢失
    // - 通过检查LSR寄存器的THRE位，确保发送寄存器为空
    //
    // 这个循环会一直执行，直到UART准备好接收下一个字符
    while((ReadReg(UART_LSR) & LSR_TX_IDLE) == 0)
        ;  // 空循环，什么都不做，只是等待
    
    // ========================================
    // 发送字符
    // ========================================
    //
    // 将字符写入发送保持寄存器(THR)
    // UART会自动将字符发送出去
    WriteReg(UART_THR, c);
}

// ========================================
// 字符串输出函数
// ========================================
//
// 向UART发送一个以null结尾的字符串
// s：指向字符串的指针
void uart_puts(char *s)
{
    // 遍历字符串中的每个字符
    while(*s) {  // 当*s不为0（null字符）时继续循环
        // 发送当前字符
        uart_putc(*s);
        
        // 指针移动到下一个字符
        s++;
    }
    
    // 当遇到null字符('\0')时，循环结束
    // 这样我们就发送了整个字符串
}
